package org.dromara.exam.util;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.exam.domain.dto.TemplateSchema;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.dromara.exam.util.DoubaoScoringUtils.shortAnswerScoring;

/**
 * 答案评分评估器
 * <p>
 * 该类用于根据问卷 schema 和用户答案来计算每个问题的得分以及总分。
 * 它通过一系列的评估策略来确定每个问题的得分方式。
 *
 * @author xiyin321
 */
@Slf4j // 使用 Lombok 的 @Slf4j 注解自动生成日志记录器
public class AnswerScoreEvaluator {

    // 问卷 schema，包含了问题的结构和评分标准
    private final TemplateSchema schema;

    // 当前答案，以问题ID为键，用户选择的答案为值
    private final LinkedHashMap answer;

    // 评估器列表，包含了不同的评分策略
    List<Evaluator> evaluatorList = new ArrayList<>();


    /**
     * -- GETTER --
     * 获取问题的分值
     *
     * @return 问题得分映射
     */
    // 问题得分映射，记录每个问题的得分
    @Getter
    LinkedHashMap<String, Double> questionScore = new LinkedHashMap<>();

    public AnswerScoreEvaluator(TemplateSchema schema, LinkedHashMap answer) {
        this.schema = schema; // 初始化问卷 schema
        this.answer = answer; // 初始化用户答案

        // 添加各种评分策略到 evaluatorList 中
        evaluatorList.add(new OnlyOneEvaluator());
        evaluatorList.add(new SelectAllEvaluator());
        evaluatorList.add(new SelectCorrectEvaluator());
        evaluatorList.add(new SelectEvaluator());
        evaluatorList.add(new ShortAnswerEvaluator());
    }

    /**
     * 计算总分
     *
     * @return 总分
     */
    public Double eval() {
        return doEval(schema); // 调用私有方法 doEval 来计算总分
    }

    /**
     * 执行评分逻辑
     * <p>
     * 根据问题类型和用户答案计算得分，并累加子问题的得分
     *
     * @param qSchema 问题 schema
     * @return 问题得分
     */
    private Double doEval(TemplateSchema qSchema) {
        // 查找支持当前问题类型的评估器并计算得分，否则使用默认评估器
        double score = evaluatorList.stream().filter(x -> x.support(qSchema)).findFirst()
            .orElse(new DefaultEvaluator()).eval(qSchema);
        // 如果问题是数据类型，则将得分记录到 questionScore 中
        if (TemplateSchema.QuestionType.dataType().contains(qSchema.getType())) {
            questionScore.put(qSchema.getId(), score);
        }
        // 如果问题有子问题，递归处理子问题并累加得分
        if (qSchema.getChildren() != null) {
            for (TemplateSchema child : qSchema.getChildren()) {
                score += doEval(child);
            }
        }
        return score; // 返回最终得分
    }


    /**
     * 评估器接口
     */
    interface Evaluator {

        /**
         * 计算问题得分
         *
         * @param qSchema 问题 schema
         * @return 问题得分
         */
        Double eval(TemplateSchema qSchema);

        /**
         * 检查是否支持当前评分策略
         *
         * @param qSchema 问题 schema
         * @return 是否支持
         */
        boolean support(TemplateSchema qSchema);

    }

    /**
     * 单选题评估器
     */
    class OnlyOneEvaluator implements Evaluator {

        /**
         * 评估单选题得分
         *
         * @param qSchema 问题 schema
         * @return 问题得分
         */
        @Override
        public Double eval(TemplateSchema qSchema) {
            System.out.println("单选题评估器");
            String qId = qSchema.getId(); // 获取问题 ID
            Double qScore = qSchema.getAttribute().getExamScore(); // 获取问题的满分
            Map qValue = (Map) answer.get(qId); // 获取用户的答案
            // 遍历选项，如果有一个正确答案且用户只选择了这一个选项，则返回满分
            for (TemplateSchema option : qSchema.getChildren()) {
                if (option.getAttribute().getExamCorrectAnswer() != null && qValue.containsKey(option.getId())
                    && qValue.values().size() == 1) {
                    return qScore;
                }
            }
            return .0; // 否则返回 0 分
        }

        /**
         * 检查是否支持单选题评分策略
         *
         * @param qSchema 问题 schema
         * @return 是否支持
         */
        @Override
        public boolean support(TemplateSchema qSchema) {
            if (qSchema.getAttribute() == null) {
                return false; // 如果没有属性信息，返回不支持
            }
            // 检查是否是单选题模式且问题和答案都有效
            return TemplateSchema.ExamScoreMode.onlyOne.equals(qSchema.getAttribute().getExamAnswerMode())
                && qSchema.getAttribute().getExamScore() != null && answer.get(qSchema.getId()) != null;
        }

    }

    /**
     * 全选题评估器
     */
    class SelectAllEvaluator implements Evaluator {

        /**
         * 评估全选题得分
         *
         * @param qSchema 问题 schema
         * @return 问题得分
         */
        @Override
        public Double eval(TemplateSchema qSchema) {
            String qId = qSchema.getId(); // 获取问题 ID
            Double qScore = qSchema.getAttribute().getExamScore(); // 获取问题的满分
            Map qValue = (Map) answer.get(qId); // 获取用户的答案
            // 遍历选项，检查所有选项是否都正确选择
            for (TemplateSchema option : qSchema.getChildren()) {
                // 如果选择了非正确答案，直接返回 0 分
                if (option.getAttribute().getExamCorrectAnswer() == null && qValue.containsKey(option.getId())) {
                    return .0;
                }
                // 如果正确答案未被选择，直接返回 0 分
                if (option.getAttribute().getExamCorrectAnswer() != null && !qValue.containsKey(option.getId())) {
                    return .0;
                }
                Object optionValue = qValue.get(option.getId());
                // 如果选项值不匹配正确答案，直接返回 0 分
                if (option.getAttribute().getExamCorrectAnswer() != null
                    && !optionValueMatchCorrectAnswer(qSchema, option.getId(), optionValue)) {
                    return .0;
                }
            }
            return qScore; // 所有条件都满足，返回满分
        }


        /**
         * 检查是否支持全选题评分策略
         *
         * @param qSchema 问题 schema
         * @return 是否支持
         */
        @Override
        public boolean support(TemplateSchema qSchema) {
            if (qSchema.getAttribute() == null) {
                return false; // 如果没有属性信息，返回不支持
            }
            // 检查是否是全选题模式且问题和答案都有效
            return TemplateSchema.ExamScoreMode.selectAll.equals(qSchema.getAttribute().getExamAnswerMode())
                && qSchema.getAttribute().getExamScore() != null && answer.get(qSchema.getId()) != null;
        }

    }

    /**
     * 正确答案评估器
     */
    class SelectCorrectEvaluator implements Evaluator {

        /**
         * 评估正确答案得分
         *
         * @param qSchema 问题 schema
         * @return 问题得分
         */
        @Override
        public Double eval(TemplateSchema qSchema) {
            System.out.println("触发正确答案评估器");
            String qId = qSchema.getId(); // 获取问题 ID
            Map qValue = (Map) answer.get(qId); // 获取用户的答案
            double sum = 0; // 初始化得分
            // 遍历选项，计算正确答案的得分
            for (TemplateSchema option : qSchema.getChildren()) {
                // 如果是非填空题且选择了错误答案，直接返回 0 分
                if (!isBlank(qSchema) && option.getAttribute().getExamCorrectAnswer() == null
                    && qValue.containsKey(option.getId())) {
                    return .0;
                }
                Object optionValue = qValue.get(option.getId());
                // 如果选项值匹配正确答案，累加得分
                if (optionValueMatchCorrectAnswer(qSchema, option.getId(), optionValue)) {
                    sum += option.getAttribute().getExamScore();
                }
            }
            return sum; // 返回累加后的得分
        }

        /**
         * 检查是否支持正确答案评分策略
         *
         * @param qSchema 问题 schema
         * @return 是否支持
         */
        @Override
        public boolean support(TemplateSchema qSchema) {
            if (qSchema.getAttribute() == null) {
                return false; // 如果没有属性信息，返回不支持
            }
            // 检查是否是正确答案模式且问题和答案都有效
            return TemplateSchema.ExamScoreMode.selectCorrect.equals(qSchema.getAttribute().getExamAnswerMode())
                && qSchema.getAttribute().getExamScore() != null && answer.get(qSchema.getId()) != null;
        }

    }

    /**
     * 选择题评估器
     */
    class SelectEvaluator implements Evaluator {

        /**
         * 评估选择题得分
         *
         * @param qSchema 问题 schema
         * @return 问题得分
         */
        @Override
        public Double eval(TemplateSchema qSchema) {
            System.out.println("选择题评估器");
            String qId = qSchema.getId(); // 获取问题 ID
            Map qValue = (Map) answer.get(qId); // 获取用户的答案
            double sum = 0; // 初始化得分
            // 遍历选项，计算正确答案的得分
            for (TemplateSchema option : qSchema.getChildren()) {
                Object optionValue = qValue.get(option.getId());
                // 如果选项值匹配正确答案，累加得分
                if (optionValueMatchCorrectAnswer(qSchema, option.getId(), optionValue)) {
                    Double score = option.getAttribute().getExamScore();
                    if (score == null) {
                        score = .0;
                        log.warn("问卷{}问题{}选项{}未设置分值", schema.getId(), qSchema.getId(), option.getId()); // 记录警告日志
                    }
                    sum += score;
                }
            }
            return sum; // 返回累加后的得分
        }

        /**
         * 检查是否支持选择题评分策略
         *
         * @param qSchema 问题 schema
         * @return 是否支持
         */
        @Override
        public boolean support(TemplateSchema qSchema) {
            if (qSchema.getAttribute() == null) {
                return false; // 如果没有属性信息，返回不支持
            }
            // 检查是否是选择题模式且问题和答案都有效
            return TemplateSchema.ExamScoreMode.select.equals(qSchema.getAttribute().getExamAnswerMode())
                && qSchema.getAttribute().getExamScore() != null && answer.get(qSchema.getId()) != null;
        }

    }

    /**
     * 简答题评估器
     */
    class ShortAnswerEvaluator implements Evaluator {

        @Override
        public Double eval(TemplateSchema qSchema) {
            String qId = qSchema.getId(); // 获取问题 ID
            Double qScore = qSchema.getAttribute().getExamScore(); // 获取问题的满分
            String referenceAnswer = ""; // 获取参考答案
            Object userAnswer = null;
            Map qValue = (Map) answer.get(qId); // 获取用户的答案
            for (TemplateSchema option : qSchema.getChildren()) {
                if (option.getAttribute().getExamCorrectAnswer() == null && qValue.containsKey(option.getId())) {
                    return .0;
                }
                referenceAnswer = option.getAttribute().getExamCorrectAnswer();
                userAnswer = qValue.get(option.getId());
            }

            if (userAnswer == null) {
                return .0;
            }
            return shortAnswerScoring(qSchema.getTitle(), qScore, referenceAnswer, userAnswer); // 返回计算后的得分
        }

        @Override
        public boolean support(TemplateSchema qSchema) {
            if (qSchema.getAttribute() == null) {
                return false; // 如果没有属性信息，返回不支持
            }
            // 检查是否是简答题模式且问题和答案都有效
            return TemplateSchema.ExamScoreMode.ai.equals(qSchema.getAttribute().getExamAnswerMode())
                && qSchema.getAttribute().getExamScore() != null && answer.get(qSchema.getId()) != null;
        }
    }

    /**
     * 默认评估器
     */
    class DefaultEvaluator implements Evaluator {

        @Override
        public Double eval(TemplateSchema qSchema) {
            return 0.0; // 默认返回 0 分
        }

        @Override
        public boolean support(TemplateSchema qSchema) {
            return true; // 默认评估器总是支持
        }
    }

    /**
     * 检查选项答案是否匹配正确答案
     *
     * @param qSchema     问题 schema
     * @param optionId    选项 id
     * @param optionValue 选项值
     * @return 是否匹配
     */
    private boolean optionValueMatchCorrectAnswer(TemplateSchema qSchema, String optionId, Object optionValue) {
        if (optionValue == null) {
            return false; // 如果选项值为空，返回不匹配
        }
        // 如果不是填空题，直接返回匹配
        if (!isBlank(qSchema)) {
            return true;
        }
        TemplateSchema.ExamMatchRule matchRule = qSchema.getAttribute().getExamMatchRule(); // 获取匹配规则
        TemplateSchema optionSchema = qSchema.getChildren().stream().filter(x -> x.getId().equals(optionId))
            .findFirst().get(); // 获取选项 schema
        String correctAnswer = optionSchema.getAttribute().getExamCorrectAnswer(); // 获取正确答案
        if (correctAnswer == null) {
            log.warn("项目{}问题{}选项{}未设置正确答案", schema.getId(), qSchema.getId(), optionSchema.getId()); // 记录警告日志
            // 如果正确答案为空，返回不匹配
            return false;
        }
        // 多个正确答案可以按照（回车键）隔开，只要有一个正确即可
        for (String item : correctAnswer.split("\n")) {
            if (TemplateSchema.ExamMatchRule.completeSame.equals(matchRule) && item.equals(optionValue.toString())) {
                return true; // 完全相同匹配
            }
            if (TemplateSchema.ExamMatchRule.contain.equals(matchRule) && item.contains(optionValue.toString())) {
                return true; // 包含匹配
            }
        }
        return false; // 不匹配
    }

    /**
     * 检查是否是填空题
     *
     * @param qSchema 问题 schema
     * @return 是否是填空题
     */
    private boolean isBlank(TemplateSchema qSchema) {
        // 如果问题类型不是单选题、多选题、判断题或选择题，则认为是填空题
        return !TemplateSchema.QuestionType.Radio.equals(qSchema.getType())
            && !TemplateSchema.QuestionType.Checkbox.equals(qSchema.getType())
            && !TemplateSchema.QuestionType.TrueFalse.equals(qSchema.getType())
            && !TemplateSchema.QuestionType.Select.equals(qSchema.getType())
            && !TemplateSchema.QuestionType.Textarea.equals(qSchema.getType());
    }

}
